package cn.org.xuanguang.kepler.hormos.spring.starter;

import cn.org.xuanguang.kepler.hormos.orm.dao.DefaultSyncRepository;
import cn.org.xuanguang.kepler.hormos.rdb.entity.GenericEntity;
import cn.org.xuanguang.kepler.hormos.rdb.jpa.Id;
import cn.org.xuanguang.kepler.hormos.rdb.jpa.Table;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.index.CandidateComponentsIndex;
import org.springframework.context.index.CandidateComponentsIndexLoader;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 通过注解开启 ORM 注入管理，仿照 Hibernate 实现。
 * 需要所有被管理类继承 HormsBaseSyncRepository。
 */
@Slf4j
public class HormsRepositoryRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * 初始化一个资源模式解析器，用于后续的资源路径匹配解析。
     * 这是一个私有成员变量，其类型为 ResourcePatternResolver，具体实现为 PathMatchingResourcePatternResolver。
     * 这个解析器主要用于根据指定的模式解析资源路径，返回匹配的资源对象。
     */
    private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();


    /**
     * 这段代码声明了一个私有的、最终的MetadataReaderFactory实例，
     * 用于创建和管理MetadataReader的缓存。MetadataReaderFactory是一个
     * 工厂类，用于生成MetadataReader实例，这些实例可以用来读取和解析类的元数据。
     * 具体实现为CachingMetadataReaderFactory，意味着它将缓存读取的元数据，
     * 以提高后续相同资源的元数据读取效率。
     */
    private final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();

    /**
     * 根据资源对象解析出对应的类名。
     *
     * @param resource 资源对象
     * @return 类名，如果解析失败则返回 null
     */
    private String getResourceClassName(Resource resource) {
        try {
            return metadataReaderFactory
                    .getMetadataReader(resource)
                    .getClassMetadata()
                    .getClassName();
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 根据包名获取资源对象的流。
     *
     * @param packageStr 包名数组
     * @return 资源对象的流
     * @throws Exception 抛出异常
     */
    @SneakyThrows
    private Stream<Resource> doGetResources(String packageStr) {
        String path = ResourcePatternResolver
                .CLASSPATH_ALL_URL_PREFIX
                .concat(packageStr.replace(".", "/")).concat("/**/*.class");
        return Arrays.stream(resourcePatternResolver.getResources(path));
    }

    /**
     * 扫描实体类。
     *
     * @param packageStr 包名数组
     * @return 实体类的集合
     */
    protected Set<String> scanEntities(String[] packageStr) {
        CandidateComponentsIndex index = CandidateComponentsIndexLoader.loadIndex(org.springframework.util.ClassUtils.getDefaultClassLoader());
        if (null == index) {
            return Stream
                    .of(packageStr)
                    .flatMap(this::doGetResources)
                    .map(this::getResourceClassName)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
        }

        return Stream
                .of(packageStr)
                .flatMap(pkg -> index.getCandidateTypes(pkg, Table.class.getName()).stream())
                .collect(Collectors.toSet());
    }

    /**
     * 查找实体类的 ID 类型。
     *
     * @param entityType 实体类型
     * @return ID 类型
     */
    private Class<?> findIdType(Class<?> entityType) {
        Class<?> idType;
        try {
            if (GenericEntity.class.isAssignableFrom(entityType)) {
                return GenericTypeResolver.resolveTypeArgument(entityType, GenericEntity.class);
            }

            Class<?>[] ref = new Class[1];
            ReflectionUtils.doWithFields(entityType, field -> {
                if (field.isAnnotationPresent(Id.class)) {
                    ref[0] = field.getType();
                }
            });
            idType = ref[0];

            if (idType == null) {
                Method getId = org.springframework.util.ClassUtils.getMethod(entityType, "getId");
                idType = getId.getReturnType();
            }
        } catch (Throwable e) {
            log.warn("unknown id type of entity:{}", entityType);
            idType = String.class;
        }

        return idType;

    }

    /**
     * 注册 Bean 定义。
     *
     * @param importingClassMetadata 注解元数据
     * @param registry               Bean 定义注册表
     * @throws Exception 抛出异常
     */
    @Override
    @SneakyThrows
    @SuppressWarnings("all")
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        Map<String, Object> attr = importingClassMetadata.getAnnotationAttributes(EnableHormsRepository.class.getName());
        if (attr == null) {
            return;
        }

        String[] arr = (String[]) attr.get("value");

        Class<Annotation>[] anno = (Class[]) attr.get("annotation");

        Set<EntityInfo> entityInfos = new HashSet<>();
        CandidateComponentsIndex index = CandidateComponentsIndexLoader.loadIndex(org.springframework.util.ClassUtils.getDefaultClassLoader());
        for (String className : scanEntities(arr)) {
            Class<?> entityType = org.springframework.util.ClassUtils.forName(className, null);
            if (Arrays.stream(anno)
                    .noneMatch(ann -> AnnotationUtils.getAnnotation(entityType, ann) != null)) {
                continue;
            }


            Class idType = findIdType(entityType);

            EntityInfo entityInfo = new EntityInfo(entityType,
                    entityType,
                    idType);
            if (!entityInfos.contains(entityInfo)) {
                entityInfos.add(entityInfo);
            }

        }
        for (EntityInfo entityInfo : entityInfos) {
            Class entityType = entityInfo.getEntityType();
            Class idType = entityInfo.getIdType();
            Class realType = entityInfo.getRealType();
            /*
             * 注册 默认的同步处理器，如果需要还执行器，可在这添加
             */
            log.debug("register SyncRepository<{},{}>", entityType.getName(), idType.getSimpleName());
            ResolvableType repositoryType = ResolvableType.forClassWithGenerics(DefaultSyncRepository.class, entityType, idType);
            RootBeanDefinition definition = new RootBeanDefinition();
            definition.setTargetType(repositoryType);
            definition.setBeanClass(SyncRepositoryFactoryBean.class);
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            definition.getPropertyValues().add("entityType", realType);
            registry.registerBeanDefinition(realType.getSimpleName().concat("HormsBaseSyncRepository"), definition);

        }

    }


}

